package com.example.demo.common.exception;

import com.example.demo.common.constant.StatusBusiness;
import com.example.demo.common.response.BaseResponse;
import com.example.demo.common.response.ResponseData;
import com.example.demo.util.common.ResponseDataUtil;
import com.example.demo.util.common.WebUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.IbatisException;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;
import java.net.BindException;


/**
 * @program: xy-framework
 * @description: 控制层异常统一处理
 * @author: FN
 * @create: 2019-12-06
 **/
@Slf4j
@Data
public abstract class GlobalControllerException {

    /**
     * 测试环境
     **/
    public final static String ENV_DEV = "dev";

    private String profile;

    //初始化定义父类的运行环境,配置报错的详细信息还是概要信息
    @PostConstruct
    public abstract void initEnv();

    @ExceptionHandler(Exception.class)
    public ResponseData exceptionHandler(Exception e) {
        log.error(e.getMessage(), e);
        String msg = StatusBusiness.FAILD.getMessage();
        if (ENV_DEV.equals(profile)) {
            // 当为测试环境,要显示具体的错
            msg = buildMessage(e);
        } else {
            msg = buildMessages(e);
        }
        BaseResponse baseResponse = new BaseResponse(StatusBusiness.FAILD.getStatus(), msg);
        return ResponseDataUtil.baseResponseData(baseResponse);
    }

    @ExceptionHandler({
            BindException.class
    })
    public ResponseData exceptionHandler(BindException e) {
        log.error(e.getMessage(), e);
        String msg = StatusBusiness.FAILD.getMessage();
        if (ENV_DEV.equals(profile)) {
            // 当为测试环境,要显示具体的错
            msg = buildMessage(e);
        } else {
            msg = buildMessages(e);
        }
        BaseResponse baseResponse = new BaseResponse(StatusBusiness.FAILD.getStatus(), msg);
        return ResponseDataUtil.baseResponseData(baseResponse);
    }

    @ExceptionHandler({
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotWritableException.class,
            HttpMediaTypeNotAcceptableException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class,
            HttpRequestMethodNotSupportedException.class,
            NullPointerException.class
    })

    public ResponseData exceptionHandlers(Exception e) {
        log.error(e.getMessage(), e);
        String msg = StatusBusiness.FAILD.getMessage();
        if (ENV_DEV.equals(profile)) {
            // 当为测试环境,要显示具体的错
            msg = buildMessage(e);
        } else {
            msg = buildMessages(e);
        }
        BaseResponse baseResponse = new BaseResponse(StatusBusiness.FAILD.getStatus(), msg);
        return ResponseDataUtil.baseResponseData(baseResponse);
    }

    @ExceptionHandler({
            HttpMessageNotReadableException.class
    })

    public ResponseData exceptionHandler(HttpMessageNotReadableException e) {
        log.error(e.getMessage(), e);
        BaseResponse baseResponse = new BaseResponse(StatusBusiness.INVALIDATE.getStatus(), StatusBusiness.INVALIDATE.getMessage());
        return ResponseDataUtil.baseResponseData(baseResponse);
    }

    @ExceptionHandler({
            //参数校验异常
            ValidationException.class
    })
    public ResponseData exceptionHandler(ValidationException e) {
        log.error(e.getMessage(), e);
        String msg = e.getCause().getMessage();
        BaseResponse baseResponse = new BaseResponse(StatusBusiness.INVALIDATE.getStatus(), msg);
        return ResponseDataUtil.baseResponseData(baseResponse);
    }

    @ExceptionHandler({
            IbatisException.class,
    })
    public ResponseData exceptionHandler(IbatisException e) {
        log.error(e.getMessage(), e);
        String msg = e.getMessage();
        String[] split = msg.split(":");
        BaseResponse baseResponse = new BaseResponse(StatusBusiness.INVALIDATE.getStatus(), split[1].trim());
        return ResponseDataUtil.baseResponseData(baseResponse);
    }

    /**
     * 数据库执行异常  例如：参数中包含 $
     **/
    @ExceptionHandler({
            MyBatisSystemException.class
    })
    public ResponseData exceptionHandler(MyBatisSystemException e) {
        log.error(e.getMessage(), e);
        BaseResponse baseResponse = new BaseResponse(StatusBusiness.INVALIDATE.getStatus(), StatusBusiness.FAILD.getMessage());
        return ResponseDataUtil.baseResponseData(baseResponse);
    }

    /**
     * 包装绑定异常结果
     *
     * @param bindingResult 绑定结果
     * @return 异常结果
     */
    private String wrapperBindingResult(BindingResult bindingResult) {
        StringBuilder msg = new StringBuilder();

        for (ObjectError error : bindingResult.getAllErrors()) {
            msg.append(", ");
            if (error instanceof FieldError) {
                msg.append(((FieldError) error).getField()).append(": ");
            }
            msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());

        }

        return msg.substring(2);
    }

    /**
     * 构建异常信息
     *
     * @param ex
     * @return
     */
    protected String buildMessage(Throwable ex) {
        HttpServletRequest request = WebUtils.getRequest();
        StringBuilder message = new StringBuilder("请求处理失败 [ ");
        message.append(request.getMethod());
        message.append(" ");
        message.append(request.getRequestURI());
        message.append(" ]");
        if (null != ex && null != ex.getMessage()) {
            message.append(" : ");
            message.append(ex.getMessage());
        }
        return message.toString();
    }

    protected String buildMessages(Throwable ex) {
        HttpServletRequest request = WebUtils.getRequest();
        StringBuilder message = new StringBuilder();
        if (null != ex && null != ex.getMessage()) {
            message.append(ex.getMessage());
        }
        return message.toString();
    }

    /**
     * 构建异常信息
     *
     * @param messages
     * @return
     */
    private String buildMessage(String messages) {
        HttpServletRequest request = WebUtils.getRequest();
        StringBuilder message = new StringBuilder("请求处理失败 [ ");
        message.append(request.getMethod());
        message.append(" ");
        message.append(request.getRequestURI());
        message.append(" ]");
        message.append(" : ");
        message.append(messages);
        return message.toString();
    }
}
